﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Security.Permissions;
using System.Text;

namespace Itfort
{
	/// <summary>
	/// 
	/// </summary>
	/// <typeparam name="TExceptionArgs"></typeparam>
	[Serializable]
	public sealed class Exception<TExceptionArgs> : Exception, ISerializable
	   where TExceptionArgs : ExceptionArgs
	{
		private const String c_args = "Args";  // For (de)serialization 
		private readonly TExceptionArgs m_args;

		/// <summary>
		/// 
		/// </summary>
		public TExceptionArgs Args
		{
			get
			{
				return m_args;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="message"></param>
		/// <param name="innerException"></param>
		public Exception(String message = null, Exception innerException = null)
			: this(null, message, innerException)
		{

		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="args"></param>
		/// <param name="message"></param>
		/// <param name="innerException"></param>
		public Exception(TExceptionArgs args, String message = null, Exception innerException = null)
			: base(message, innerException)
		{
			m_args = args;
		}

		/// <summary>
		/// The constructor is for deserialization; since the class is sealed, the constructor is 
		/// private. If this class were not sealed, this constructor should be protected  
		/// </summary>
		/// <param name="info"></param>
		/// <param name="context"></param>
		[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
		private Exception(SerializationInfo info, StreamingContext context)
			: base(info, context)
		{
			m_args = (TExceptionArgs)info.GetValue(c_args, typeof(TExceptionArgs));
		}

		/// <summary>
		/// The method for serialization; it’s public because of the ISerializable interface 
		/// </summary>
		/// <param name="info"></param>
		/// <param name="context"></param>
		[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
		public override void GetObjectData(SerializationInfo info, StreamingContext context)
		{
			info.AddValue(c_args, m_args);
			base.GetObjectData(info, context);
		}

		/// <summary>
		/// 
		/// </summary>
		public override String Message
		{
			get
			{
				String baseMsg = base.Message;
				return (m_args == null) ? baseMsg : baseMsg + " (" + m_args.Message + ")";
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="obj"></param>
		/// <returns></returns>
		public override Boolean Equals(Object obj)
		{
			Exception<TExceptionArgs> other = obj as Exception<TExceptionArgs>;
			if (obj == null) return false;
			return Object.Equals(m_args, other.m_args) && base.Equals(obj);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public override int GetHashCode() { return base.GetHashCode(); }
	}

	/// <summary>
	/// 
	/// </summary>
	[Serializable]
	public abstract class ExceptionArgs
	{
		/// <summary>
		/// 
		/// </summary>
		public virtual String Message
		{
			get
			{
				return String.Empty;
			}
		}
	}
}
